package org.ziegler.log.analysis.cmdParser;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import joptsimple.AbstractOptionSpec;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpecBuilder;
import org.ziegler.log.analysis.LogCore;

public class CmdOptionParser {

    private static final CmdOptionParser CMD_OPTION_PARSER = new CmdOptionParser();

    public static CmdOptionParser getInstance() {
        return CMD_OPTION_PARSER;
    }


    /** cmd解析器 */
    private OptionParser parser;
    /** 解析结果集 */
    private OptionSet optionSet;

    private final OptionSpecBuilder cacheOptionSpec;

    private final ArgumentAcceptingOptionSpec<String> pathOptionSpec;
    private final ArgumentAcceptingOptionSpec<Integer> threadNumOptionSpec;

    private final ArgumentAcceptingOptionSpec<FilterType> filterTypeOptionSpec;
    private final ArgumentAcceptingOptionSpec<ResultFilterType> resultFilterTypeOptionSpec;

    private final ArgumentAcceptingOptionSpec<ShowType> showTypeOptionSpec;

    private final AbstractOptionSpec<Void> helpOptionSpec;
    private final ArgumentAcceptingOptionSpec<String> errKeyOptionSpec;
    private final ArgumentAcceptingOptionSpec<String> datetimeOptionSpec;

    /** 服务器区id */
    private final ArgumentAcceptingOptionSpec<Integer> serverIdOptionSpec;

    /** 限制数量 */
    private final ArgumentAcceptingOptionSpec<Integer> limitOptionSpec;




    private CmdOptionParser() {
        parser = new OptionParser();

        helpOptionSpec = parser.acceptsAll(List.of("h", "?"), "显示帮助").forHelp();

        cacheOptionSpec = parser.accepts("cache", "缓存已分析日志文件和ErrorKey");

        pathOptionSpec = parser.accepts("path", "日志文件夹路径")
                .withRequiredArg().ofType(String.class).describedAs("");
        threadNumOptionSpec = parser.accepts("threadNum", "运算线程池数量")
                .withRequiredArg().ofType(Integer.class).describedAs("(0, 100]");

        // filter
        filterTypeOptionSpec = parser.accepts("filter", "过滤器类型")
                .withRequiredArg().ofType(FilterType.class).describedAs(StringUtils.join(FilterType.values(), ","));

        // resultFilter
        resultFilterTypeOptionSpec = parser
                .accepts("resultFilter", "结果过滤器类型，多个用“,”分隔。")
                .withRequiredArg().ofType(ResultFilterType.class)
                .withValuesSeparatedBy(",")
                .describedAs(StringUtils.join(ResultFilterType.values(), ","));

        // datetime
        datetimeOptionSpec = parser.accepts("datetime", "过滤器使用日期格式：2020-7-17")
                .withRequiredArg().ofType(String.class)
                .describedAs("");

        // show
        showTypeOptionSpec = parser.accepts("show", "结果展示模式，多种显示用“,”分隔。")
                .withRequiredArg().ofType(ShowType.class)
                .withValuesSeparatedBy(",")
                .describedAs(StringUtils.join(ShowType.values(), ","));

        // errorKey
        errKeyOptionSpec = parser
                .accepts("errKey", "当filter指定为ERROR时，使用此项获取ErrKey，支持多个，用“,”分隔").withRequiredArg()
                .ofType(String.class).withValuesSeparatedBy(",")
                .describedAs("");

        serverIdOptionSpec = parser.accepts("serverId", "服务器区id").withRequiredArg()
                .ofType(Integer.class).describedAs("");

        limitOptionSpec = parser.accepts("limit", "限制数量").withRequiredArg()
                .ofType(Integer.class).describedAs("");

        parser.allowsUnrecognizedOptions();
    }

    public void printHelpOn() {
        try {
            parser.printHelpOn(System.out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void parse(String... arguments) {
        LogCore.debug("命令行参数：" + StringUtils.join(arguments, " "));

        optionSet = parser.parse(arguments);
    }

    public boolean hasFilter() {
        return optionSet.has(filterTypeOptionSpec) && optionSet.hasArgument(filterTypeOptionSpec);
    }

    public FilterType getFilterType() {
        if (hasFilter()) {
            return filterTypeOptionSpec.value(optionSet);
        }

        return FilterType.EMPTY;
    }

    public boolean hasCache() {
        return optionSet.has(cacheOptionSpec);
    }

    public boolean hasPath() {
        return optionSet.has(pathOptionSpec) && optionSet.hasArgument(pathOptionSpec);
    }

    public String getPath() {
        if (hasPath()) {
            return pathOptionSpec.value(optionSet);
        }

        return StringUtils.EMPTY;
    }

    public boolean hasThreadNum() {
        return optionSet.has(threadNumOptionSpec) && optionSet.hasArgument(threadNumOptionSpec);
    }

    public int getThreadNum() {
        if (hasThreadNum()) {
            int threadNum = threadNumOptionSpec.value(optionSet);
            return threadNum;
        }

        return 1;
    }

    public boolean hasResultFilter() {
        return optionSet.has(resultFilterTypeOptionSpec) && optionSet.hasArgument(resultFilterTypeOptionSpec);
    }

    public List<ResultFilterType> getResultFilterTypes() {
        if (hasResultFilter()) {
            return resultFilterTypeOptionSpec.values(optionSet);
        }

        return Collections.emptyList();
    }

    public boolean hasShow() {
        return optionSet.has(showTypeOptionSpec) && optionSet.hasArgument(showTypeOptionSpec);
    }

    public List<ShowType> getShowTypes() {
        if (hasShow()) {
            return showTypeOptionSpec.values(optionSet);
        }

        return Collections.emptyList();
    }

    public boolean isHelp() {
        return optionSet.has(helpOptionSpec);
    }

    public boolean hasErrorKey() {
        return optionSet.has(errKeyOptionSpec) && optionSet.hasArgument(errKeyOptionSpec);
    }

    public List<String> getAllErrKey() {
        if (hasErrorKey()) {
            return errKeyOptionSpec.values(optionSet);
        }

        return Collections.emptyList();
    }

    public boolean hasDatetime() {
        return optionSet.has(datetimeOptionSpec) && optionSet.hasArgument(datetimeOptionSpec);
    }

    public String getDateTime() {
        if (hasDatetime()) {
            return datetimeOptionSpec.value(optionSet);
        }

        return StringUtils.EMPTY;
    }

    public boolean hasServerId() {
        return optionSet.has(serverIdOptionSpec) && optionSet
                .hasArgument(serverIdOptionSpec);
    }

    public int getServerId() {
        if (hasServerId()) {
            return serverIdOptionSpec.value(optionSet);
        }

        return 0;
    }

    public boolean hasLimit() {
        return optionSet.has(limitOptionSpec) && optionSet
                .hasArgument(limitOptionSpec);
    }

    public int getLimit() {
        if (hasLimit()) {
            return limitOptionSpec.value(optionSet);
        }
        return 0;
    }

}
